package com.ruoyi.common.core.utils.map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.*;

/**
 * map工具类
 */
public class JavaBeanUtil {

    private static Logger logger = LoggerFactory.getLogger(JavaBeanUtil.class);

    /**
     * 对象转换实体
     *
     * @param obj 待转换对象
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, Object> convertBeanToMap(Object obj) {
        Map<String, Object> reMap = new HashMap<>();
        if (obj == null)
            return null;
        List<Field> fields = new ArrayList<>();
        List<Field> childFields;
        List<String> fieldsName = new ArrayList<>();
        Class tempClass = obj.getClass();
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        childFields = Arrays.asList(obj.getClass().getDeclaredFields());
        for (Field field : childFields) {
            fieldsName.add(field.getName());
        }
        try {
            for (Field field : fields) {
                try {
                    if (fieldsName.contains(field.getName())) {
                        Field f = obj.getClass().getDeclaredField(
                                field.getName());
                        f.setAccessible(true);
                        Object o = f.get(obj);
                        reMap.put(field.getName(), o);
                    } else {
                        Field f = obj.getClass().getSuperclass().getDeclaredField(
                                field.getName());
                        f.setAccessible(true);
                        Object o = f.get(obj);
                        reMap.put(field.getName(), o);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return reMap;
    }

    /**
     * map转换成实体
     *
     * @param clazz 实体类
     * @param map   待转换map
     * @param <T>
     * @return
     */
    public static <T> T convertMapToBean(Class<T> clazz, Map<String, Object> map) {
        T obj = null;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            obj = clazz.newInstance(); // 创建 JavaBean 对象
            // 给 JavaBean 对象的属性赋值
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                if (map.containsKey(propertyName)) {
                    // 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。
                    Object value = map.get(propertyName);
                    if ("".equals(value)) {
                        value = null;
                    }
                    Object[] args = new Object[1];
                    args[0] = value;
                    descriptor.getWriteMethod().invoke(obj, args);
                }
            }
        } catch (IllegalAccessException e) {
            logger.error("convertMapToBean 实例化JavaBean失败 Error{}", e);
        } catch (IntrospectionException e) {
            logger.error("convertMapToBean 分析类属性失败 Error{}", e);
        } catch (IllegalArgumentException e) {
            logger.error("convertMapToBean 映射错误 Error{}", e);
        } catch (InstantiationException e) {
            logger.error("convertMapToBean 实例化 JavaBean 失败 Error{}", e);
        } catch (InvocationTargetException e) {
            logger.error("convertMapToBean字段映射失败 Error{}", e);
        } catch (Exception e) {
            logger.error("convertMapToBean Error{}", e);
        }
        return (T) obj;
    }

    /**
     * 实体A 转换成实体B
     *
     * @param object 实体类A
     * @param cls    实体类B
     * @param <T>
     * @returnT
     */
    public static <T> T convertToJavaBean(Object object, Class<T> cls) {
        if (object != null) {
            Class<?> sourceClass = object.getClass();
            List<Field> sourceFields = getAllFields(sourceClass);
            List<Field> targetFields = getAllFields(cls);
            try {
                T target = cls.newInstance();
                for (Field field : targetFields) {
                    if (field.getType() != List.class && field.getType() != Map.class && !Modifier.isFinal(field.getModifiers())) {
                        Field sourceField = sourceFields.stream().filter(tmp -> tmp.getName().equals(field.getName())).findFirst().orElse(null);
                        if (sourceField != null && field.getType() == sourceField.getType()) {
                            sourceField.setAccessible(true);
                            Object value = sourceField.get(object);
                            if (value != null) {
                                field.setAccessible(true);
                                field.set(target, sourceField.get(object));
                            }
                        }
                    }
                }
                return target;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取字段列表
     *
     * @param cls
     * @return
     */
    public static List<Field> getAllFields(Class cls) {
        List<Field> fields = new ArrayList<>();
        Class tmp = cls;
        while (tmp != null) {
            fields.addAll(Arrays.asList(tmp.getDeclaredFields()));
            tmp = tmp.getSuperclass();
        }
        return fields;
    }

    /**
     * 将 JavaBean 对象转换为 Map 参数
     *
     * @param object
     * @return
     */
    public static Map<String, Object> convertBeanToMapParams(Object object) {
        if (object == null) {
            return null;
        }
        Map<String, Object> params = new HashMap<>();
        Class cls = object.getClass();
        List<Field> fields = getAllFields(cls);
        for (Field field : fields) {
            field.setAccessible(true);
            Type type = field.getType();
            if (!Modifier.isFinal(field.getModifiers())) {
                Object value = null;
                try {
                    value = field.get(object);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (type == Map.class) {
                    if (value != null) {
                        params.putAll((Map<String, Object>) value);
                    }
                } else {
                    params.put(field.getName(), value);
                }
            }
        }
        return params;
    }
}
